#include "precompiled.h"
#include "Physics_Scenery.h"
#include "Scenery.h"
#include "Sound_Manager.h"
#include "PhysicsCommon.h"

//........................................................
class MyEndOfStepListener : public hkReferencedObject, public hkpContactListener
{
public:
	HK_DECLARE_CLASS_ALLOCATOR( HK_MEMORY_CLASS_DEMO );

	void collisionAddedCallback( const hkpCollisionEvent& event )
	{
		registerForEndOfStepContactPointCallbacks( event );
	}

	void collisionRemovedCallback( const hkpCollisionEvent& event )
	{
		unregisterForEndOfStepContactPointCallbacks( event );
	}

	void contactPointCallback( const hkpContactPointEvent& event )
	{
		if ( event.m_contactPointProperties->m_flags & hkContactPointMaterial::CONTACT_IS_NEW )
		{
			if(  hkpContactPointEvent::TYPE_MANIFOLD_AT_END_OF_STEP )
				SoundManager::OrderASound( "bounce", false );
		}
	}
};

MyEndOfStepListener *Physics_Scenery::m_listener = 0;
//.........................................................



Physics_Scenery::Physics_Scenery(void)
{
	ptr_scenery=0;
}


Physics_Scenery::~Physics_Scenery(void)
{
}


// Create the character as a physical Havok object.
void Physics_Scenery::Create( hkpWorld* world, Scenery *s ){
	ptr_scenery = s;
	timeElapsed=0;

	if( s->GetIsStatic() ){

	}
	else{
		glm::vec3 ex = s->Get_Extents();
		glm::vec4 pos = s->GetPosition();

		// Create the box.
		hkpBoxShape* boxShape = new hkpBoxShape(hkVector4(ex.x, ex.y, ex.z));

		// Create info.
		hkpRigidBodyCinfo bodyCinfo;
		bodyCinfo.m_shape = boxShape;

		//  Calculate the mass properties for the shape
		const hkReal boxMass = 10.0f;
		hkpMassProperties massProperties;
		hkpInertiaTensorComputer::computeShapeVolumeMassProperties(boxShape, boxMass, massProperties);
		bodyCinfo.setMassProperties(massProperties);

		// Elasticity (value between 0 and 1.99, default = 0.4)
		bodyCinfo.m_restitution = (hkReal) 1.9;

		// Set our start position.
		bodyCinfo.m_position = hkVector4( pos.x, pos.y, pos.z, pos.w );

		// Create the rigid body
		rb = new hkpRigidBody(bodyCinfo);
		rb->m_uid = RigidBodyUniqueID::GenerateUniqueID();

		// Set our listener.
		rb->addContactListener( m_listener );

		// No longer need the reference on the boxShape, as the rigidBody now owns it
		// Not sure if I should release this now or later since it's technically held
		// by the structure.
		boxShape->removeReference();
	}

	world->addEntity( rb );

	RigidBodyGameObjectMap::add_pair_scenery(rb->m_uid, this);
}

void Physics_Scenery::Cleanup( hkpWorld* world ){

	world->markForWrite();

	rb->removeContactListener( m_listener );
	world->removeEntity( rb );
	rb->removeReference();

	m_listener->removeReference();
	world->unmarkForWrite();
}

// Initialize the static scenery listener for ends of physics steps.
void Physics_Scenery::CreateListener_EndOfStep(){
	if( m_listener == 0 )
		m_listener =new MyEndOfStepListener();
}

// Get any input for and step this Havok object through the simulation.
void Physics_Scenery::Update(  hkReal const &timeStep, std::vector<hkUint32>* frustum_ids ){

	// Update our elapsed time.
	hkReal elapsedTarget = 1.0f;
	timeElapsed = (timeElapsed + timeStep < elapsedTarget ) ? timeElapsed + timeStep : 0;

	// Update the scenery object's with the physical objects transformations...................
	
	hkVector4 htrans = rb->getTransform().getTranslation();
	hkRotation hrot = rb->getTransform().getRotation();

	// Use the rigid body translation.
	glm::vec4 pos = glm::vec4( htrans(0), htrans(1), htrans(2), 1 );

	// Use the rigid body rotation.
	glm::mat4 rot = glm::mat4(	hrot(0,0), hrot(0,1), hrot(0,2), 0,
								hrot(1,0), hrot(1,1), hrot(2,2), 0,
								hrot(2,0), hrot(2,1), hrot(2,2), 0,
								0,		   0,		  0,		 1 );
	
	glm::mat4 world = glm::translate(glm::mat4(1), 
		glm::vec3(pos.x, pos.y, pos.z) );

	world *= rot;
	ptr_scenery->SetWorldMatrix( world );
	ptr_scenery->Set_Position( pos );
	//...........................................................................


	// Find if we are in the viewing frustum...............................................................
	if( FRUSTUM_CULLING_ON ){
		int idpos = std::find(frustum_ids->begin(), frustum_ids->end(), rb->m_uid) - frustum_ids->begin();
		if( idpos < frustum_ids->size() ){
			//printf("scenery %d visible...\n");
			ptr_scenery->setDoDraw(true);
		}
		else{
			ptr_scenery->setDoDraw(false);
			//printf("scenery %d not visible...\n");
		}
	}
	//...........................................................................

	// Test out flashing.
	if( glfwGetKey('F') )
		ptr_scenery->setDoFlash();
}

// Load a .hkx file to give us all the physical geometry we need! Score!!!
void Physics_Scenery::Load_Create_Environment( std::string spath, hkpWorld* world  ){

	// Disable warnings
	hkError::getInstance().setEnabled(0xf03243ed, false); // 'm_contactRestingVelocity not set, setting it to REAL_MAX, so that the new collision restitution code will be disabled'
	hkError::getInstance().setEnabled(0x9fe65234, false);  // 'Unsupported simulation on type, setting to SIMULATION_TYPE_CONTINUOUS. See documentation on world stepping and time management'

	// Load the file
	hkStringBuf path( spath.c_str() );
	hkSerializeUtil::ErrorDetails loadError;
	hkResource* m_loadedData = hkSerializeUtil::load(path.cString(), &loadError);
	if ( !m_loadedData )
	{
		{
			HK_ASSERT3(0xa6451543, m_loadedData != HK_NULL, "Could not load file. The error is:\n" << loadError.defaultMessage.cString() );
		}
	}


	// Get the top level object in the file, which we know is a hkRootLevelContainer
	hkRootLevelContainer* container = m_loadedData->getContents<hkRootLevelContainer>();
	HK_ASSERT2(0xa6451543, container != HK_NULL, "Could not load root level object" );

	// Get the physics data
	hkpPhysicsData* physicsData = static_cast<hkpPhysicsData*>( container->findObjectByType( hkpPhysicsDataClass.getName() ) );
	HK_ASSERT2(0xa6451544, physicsData != HK_NULL, "Could not find physics data in root level object" );
	HK_ASSERT2(0xa6451535, physicsData->getWorldCinfo() != HK_NULL, "No physics cinfo in loaded file - cannot create a hkpWorld" );

	// Get the world and add the physics systems to it.
	{

		world->lock();

		// Add all the physics systems to the world
		for ( int i = 0; i < physicsData->getPhysicsSystems().getSize(); ++i )
		{
			world->addPhysicsSystem( physicsData->getPhysicsSystems()[i] );
		}
		world->unlock();
	}

}

hkQsTransform Physics_Scenery::createFullTransform(){

	// Create some transforms to instance the child at (including non-uniform scale)
	hkQsTransform transform1;
	
	glm::vec4 s_position = ptr_scenery->GetPosition();
	transform1.setTranslation( 
		hkVector4( s_position.x, s_position.y, s_position.z, 0) );

	glm::mat4 s_rot = glm::rotate( glm::mat4(1), ptr_scenery->GetRotation().x, glm::vec3(1,0,0) );
	s_rot = glm::rotate( s_rot, ptr_scenery->GetRotation().y, glm::vec3(0,1,0) );
	s_rot = glm::rotate( s_rot, ptr_scenery->GetRotation().z, glm::vec3(0,0,1) );

	hkRotation rot;
	rot(0,0) = hkReal(s_rot[0][0]);
	rot(0,1) = hkReal(s_rot[0][1]);
	rot(0,2) = hkReal(s_rot[0][2]);
	rot(0,3) = hkReal(s_rot[0][3]);

	rot(1,0) = hkReal(s_rot[1][0]);
	rot(1,1) = hkReal(s_rot[1][1]);
	rot(1,2) = hkReal(s_rot[1][2]);
	rot(1,3) = hkReal(s_rot[1][3]);

	rot(2,0) = hkReal(s_rot[2][0]);
	rot(2,1) = hkReal(s_rot[2][1]);
	rot(2,2) = hkReal(s_rot[2][2]);
	rot(2,3) = hkReal(s_rot[2][3]);

	rot(3,0) = hkReal(s_rot[3][0]);
	rot(3,1) = hkReal(s_rot[3][1]);
	rot(3,2) = hkReal(s_rot[3][2]);
	rot(3,3) = hkReal(s_rot[3][3]);
	transform1.setRotation( rot );

	glm::vec3 s_scale = ptr_scenery->GetScale();
	transform1.setScale( hkVector4( s_scale.x, s_scale.y, s_scale.z, 1) );

	return transform1;
}


void Physics_Scenery::setDoFlash(){
	ptr_scenery->setDoFlash();
}

Scenery* Physics_Scenery::getScenery(){
	return ptr_scenery;
}

hkpRigidBody* Physics_Scenery::getRigidBody(){
	return rb;
}